package ttt;

import java.util.ArrayList;
import java.util.Random;
class TicTacToe
{
	private static final int HUMAN        = 0; 
	private static final int COMPUTER     = 1; 
	public  static final int EMPTY        = 2;

	public  static final int HUMAN_WIN    = 0;
	public  static final int DRAW         = 1;
	public  static final int UNCLEAR      = 2;
	public  static final int COMPUTER_WIN = 3;

	private int [ ] [ ] board = new int[ 3 ][ 3 ];
    private Random random=new Random();  
	private int side=random.nextInt(2);  
	private int position=UNCLEAR;
	private char computerChar = 'X',humanChar = 'O', empty = '.';
	private char[] ch = { humanChar, computerChar, empty };
	private int[] opponents = { COMPUTER, HUMAN };
	private int[] winvalues = { COMPUTER_WIN, HUMAN_WIN };
	
	private int[] best;

	// Constructor
	public TicTacToe( )
	{
		clearBoard( );
		initSide();
	}
	
	private void initSide()
	{
	    if (this.side==COMPUTER) { ch[1]='X'; ch[0]='O'; }
		else                     { ch[1]='O'; ch[0]='X'; }
    }
    
    public void setComputerPlays()
    {
        this.side=COMPUTER;
        initSide();
    }
    
    public void setHumanPlays()
    {
        this.side=HUMAN;
        initSide();
    }

	public boolean computerPlays()
	{
	    return side==COMPUTER;
	}

	public int chooseMove()
	{
		best = new int[2];
		//minmax(board, 0);
		Best b = chooseMove( side );
		best[0] = b.row; best[1] = b.column;
	    return coordinatesToMove(best);
    }
    
    // Find optimal move
	private Best chooseMove( int side )
	{
		int opp = opponents[side], 
				simpleEval, 
				bestRow = 0, 
				bestColumn = 0, 
				value = winvalues[side];
		Best reply;

		//als er een uitkomst is...
		if( ( simpleEval = positionValue( ) ) != UNCLEAR )
			return new Best( simpleEval );			

		for (int[] move : possibleMoves(board))
        {
            place( move[0], move[1], side );
            reply = chooseMove( opp );
            place( move[0], move[1], EMPTY );
            
            if( side == COMPUTER && reply.val > value
                   ||  side == HUMAN && reply.val < value )
            {
                value = reply.val;
                bestRow = move[0]; bestColumn = move[1];
            }
        }
        
        return new Best( value, bestRow, bestColumn );
    }
	

	
   
    //check if move ok
    public boolean moveOk(int move)
    {
 	return ( move>=0 && move <=8 && board[move/3 ][ move%3 ] == EMPTY );
    }
    
    // play move
    public void playMove(int move)
    {
    	if (moveOk(move)) {
			board[move/3][ move%3] = this.side;
			if (side==COMPUTER) this.side=HUMAN;  else this.side=COMPUTER;
    	}
	}


	// Simple supporting routines
	private void clearBoard( )
	{
		for (int r = 0; r < 3; r++) {
			for (int c = 0; c < 3; c++) {
				board[r][c] = 2;
			}
		}
	}


	private boolean boardIsFull( )
	{
		for (int r = 0; r < 3; r++) {
			for (int c = 0; c < 3; c++) {
				if (board[r][c] == 2)
					return false;
			}
		}
		return true;
	}

	private boolean isAWin( int side, int[][] target)
	{
		//rijtje checken
	    for (int r = 0; r<3;r++) {
	    	int threeInARow = 0;
	    	for (threeInARow = 0; threeInARow < 3;threeInARow++) {
	    		if (target[r][threeInARow] != side)
	    			break;
	    		if (threeInARow == 2)
	    			return true;
	    			
	    	}	    	
	    }
		//kolom checken
	    for (int c = 0; c<3;c++) {
	    	int threeInARow = 0;
	    	for (threeInARow = 0; threeInARow < 3;threeInARow++) {
	    		if (target[threeInARow][c] != side)
	    			break;
	    		if (threeInARow == 2)
	    			return true;	    			
	    	}	    	
	    }
	    
	    //diagonaal
	    for (int d = 0; d<3;d++) {
	    	if (target[d][d]!=side)
	    		break;
	    	if (d==2)
	    		return true;
	    }
	    for (int d = 2; d>-1;d--) {
	    	if (target[2-d][d]!=side)
	    		break;
	    	if (d==0)
	    		return true;
	    }	    
	    return false;
	}

	// Play a move, possibly clearing a square
	private void place( int row, int column, int piece )
	{
		board[ row ][ column ] = piece;
	}

	private boolean squareIsEmpty( int row, int column )
	{
		return board[ row ][ column ] == EMPTY;
	}

	// Compute static value of current position (win, draw, etc.)
	private int positionValue( )
	{
		if (isAWin(0,board))
			return HUMAN_WIN;
		else if (isAWin(1,board))
			return COMPUTER_WIN;
		else if (boardIsFull())
			return DRAW;
		else
			return UNCLEAR;
	}
	
	
	public String toString()
	{
		String theBoard = new String("");
	    for (int r = 0; r < 3; r++) {
	    	for (int c = 0; c < 3; c++)  {
	    		theBoard += ch[board[r][c]];
	    	}
	    	theBoard += "\n";
	    }
		return theBoard;   
	}  
	
	public boolean gameOver()
	{
	    this.position=positionValue();
	    return this.position!=UNCLEAR;
    }
    
    public String winner()
    {
        if      (this.position==COMPUTER_WIN) return "computer";
        else if (this.position==HUMAN_WIN   ) return "human";
        else                                  return "nobody";
    }
    
    private int coordinatesToMove(int[] c) {
    	return (c[0]*3) + c[1];
    }
    
	
	private class Best
    {
       int row;
       int column;
       int val;

       public Best( int v )
         { this( v, 0, 0 ); }
      
       public Best( int v, int r, int c )
        { val = v; row = r; column = c; }
    }

	private ArrayList<int[]> possibleMoves(int[][] b) {
		ArrayList<int[]> moves = new ArrayList<int[]>();
		for(int r = 0; r < 3; r++) {
			for (int c = 0; c < 3; c++) {
				if (b[r][c] == 2) {
					int[] i = {r,c};
					moves.add(i);
				}
			}
		}
		return moves;		
	}	
}

